package com.wei.czz.config;

import com.alibaba.fastjson2.JSON;
import com.wei.czz.common.enums.rabbitMQ.MqEnum;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.config.RetryInterceptorBuilder;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.Objects;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2022-02-14 14:02:20
 * className: RabbitMQConfig RabbitMQ配置类
 * version: 1.0
 * description:
 *  `@EnableRabbit`：开启RabbitMQ
 */
@Configuration
@EnableRabbit
public class RabbitMQConfig {

    /**
     * 自定义rabbitmq容器工厂
     * @param connectionFactory 连接生成工厂bean
     * @param threadExecutor    线程池bean
     * @return rabbitmq容器工厂bean
     */
    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory,
                                                                               MessageConverter messageConverter,
                                                                               MessageRecoverer messageRecoverer,
                                                                               RabbitProperties rabbitProperties,
                                                                               ThreadPoolTaskExecutor threadExecutor) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        // 设置连接生成工厂类
        factory.setConnectionFactory(connectionFactory);
        // 设置任务执行线程池
        factory.setTaskExecutor(threadExecutor);
        // 设置消费者消费消息的确认模式
        factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
        // 设置每个MessageListenerContainer将会创建的consumer的最小数量，默认是1个
        factory.setConcurrentConsumers(1);
        // 设置每个MessageListenerContainer将会创建的consumer的最大数量，默认是1个
        factory.setMaxConcurrentConsumers(1);
        //
        factory.setBatchSize(1);
        // 设置消息信息格式化方式，使用JACKSON工具进行JSON序列化传输
        factory.setMessageConverter(messageConverter);

        // 连接配置对象
        RabbitProperties.Listener listener = rabbitProperties.getListener();
        // 重试配置对象
        RabbitProperties.ListenerRetry listenerRetry = listener.getSimple().getRetry();

        // 配置重试策略
        RetryInterceptorBuilder<RetryInterceptorBuilder.StatelessRetryInterceptorBuilder, RetryOperationsInterceptor> builder
                = RetryInterceptorBuilder.stateless()
                // 最大重试次数
                .maxAttempts(listenerRetry.getMaxAttempts())
                // 当达到最大重试次数后，将消息发送到死信交换机
                .recoverer(messageRecoverer)
                // 初始等待时间、乘法增长系数、最大等待时间
                .backOffOptions(listenerRetry.getInitialInterval().toMillis(), listenerRetry.getMultiplier(),
                        listenerRetry.getMaxInterval().toMillis());
        factory.setAdviceChain(builder.build());
        // 检查队列是否存在
        factory.setMissingQueuesFatal(true);
        return factory;
    }

    /**
     * 自定义消息信息格式化方式，使用JACKSON工具进行JSON序列化传输
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 自定义消息消费重试失败策略
     * @param rabbitTemplate rabbitMQ操作模板对象
     * @return 重试失败策略处理对象
     */
    @Bean
    public MessageRecoverer messageRecoverer(RabbitTemplate rabbitTemplate) {
        /*
            RejectAndDontRequeueRecoverer: 重试耗尽后，直接reject，丢弃消息。默认就是这种方式
            ImmediateRequeueMessageRecoverer:重试耗尽后，返回nack，消息重新入队
            RepublishMessageRecoverer:重试耗尽后，将失败消息投递到指定的交换机
         */
        return new RepublishMessageRecoverer(rabbitTemplate, MqEnum.DEAD.getExchange(), MqEnum.DEAD.getRoutingKey());
    }

}
